using System.Runtime.InteropServices;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy3DSoldier : EnemyBaseMovable
{
    public enum Behavior
    {
        JumpFire = 0,
        RunFire,
        LineUpFire,
        GrenadeFire,
    }
    private SeqAction behaviorAction = new();
    private SeqAction walkAction = new();
    private SeqAction standAction = new();
    private SeqAction throwGrenadeAction=new();
    private Sprite[] walkSprites;
    private Sprite[] jumpSprites;
    private Sprite standUpSprite;
    private Sprite dieSprite;

    public Behavior behavior = Behavior.RunFire;

    private float leftLimit = 93f / 16f;
    private float rightLimit = (163f) / 16f;
    private bool appeared = false;

    public Vector2 jumpDirectionSpeed=new Vector2(2f,13f);

    public int noInLineUp=1;
    public int countOfLineUp=4;
    
    public bool red;
    public ABullet.BulletType bulletType=ABullet.BulletType.None;



    void Awake()
    {
        base.Init();
        walkSprites = new Sprite[]{
            ResourcesPool.Sprites["魂-第二关-小兵-跑1"],
            ResourcesPool.Sprites["魂-第二关-小兵-跑2"],
            ResourcesPool.Sprites["魂-第二关-小兵-跑3"],
        };
        jumpSprites = new Sprite[]{
            ResourcesPool.Sprites["魂-第二关-小兵-跑3"],
            ResourcesPool.Sprites["魂-第二关-小兵-跳"],
            ResourcesPool.Sprites["魂-第二关-小兵-蹲"],
        };
        standUpSprite = ResourcesPool.Sprites["魂-第二关-小兵-站"];
        dieSprite = ResourcesPool.Sprites["魂-第二关-小兵-站"];

        //50帧移动了43个像素
        speed = 43f * 60f / 50f / 16f;

        bulletPrefab = Constant.Prefabs.EnemyBulletC.Load();
        groundLayer=1<<LayerMask.NameToLayer("3D跳台");

        dieAction=new SeqAction(){};
    }
    private void Start()
    {
        ChangeColor(this.red);
    }


    private void FixedUpdate()
    {
        base.CommonFixedUpdate();
        UpdateInViewport();
    }

    protected override void UpdateStateWalk(bool stateChanged, EnumState inState, out EnumState outState)
    {
        outState = inState;

        SeqAction.SeqUpdateAction action = null;
        switch (behavior)
        {
            case Behavior.JumpFire:
                action = JumpFireBehavior;
                break;
            case Behavior.RunFire:
                action = RunFireBehavior;
                break;
            case Behavior.LineUpFire:
                action = LineUpFireBehavior;
                break;
            case Behavior.GrenadeFire:
                action = GrenadeFireBehavior;
                break;
        }
        if (action != null)
        {
            behaviorAction.Update(action);
        }
    }
    protected override void UpdateStateDie(bool stateChanged, EnumState inState, out EnumState outState){
        outState=inState;
        dieAction.Update((SeqAction seq)=>{
            if(seq.IsFirstAction()){
                mainSR.sprite=dieSprite;
                collider2D.enabled=false;
                rig.gravityScale=0;
                rig.velocity=Vector2.zero;
            }
            if(seq.DurationTime<=11f/60f){
                float upSpeed=(16f/11f)*60f/16f;
                Vector2 position= transform.position;
                position+= seq.DeltaTime*upSpeed*Vector2.up;
                transform.position=position;
            }else if(seq.DurationTime<=14f/60f){
            }else if(seq.DurationTime<=18f/60f){
                float upSpeed=-60f/16f;
                Vector2 position= transform.position;
                position+= seq.DeltaTime*upSpeed*Vector2.up;
                transform.position=position;
            }else{
                seq.done=true;
                Instantiate(boomPrefab,transform.position,Quaternion.identity);
                Constant.Sounds.EnemyDeath1.PlayOneShot();
                if(this.red && bulletType!=ABullet.BulletType.None){
                    ThrowGun();
                }
                Destroy(gameObject);
            }
        });
    }

    protected override void PreUpdateAction(EnumState inState){}
    private void JumpFireBehavior(SeqAction seq)
    {   
        if(seq.IsFirstAction()){
            seq.PropertyInteger.Add("index",0);
        }
        int index = seq.PropertyInteger["index"];
        if(!seq.PropertyFloat.ContainsKey("indexEnterTime")){
            seq.PropertyFloat.Add("indexEnterTime",Time.time);
        }
        float durationTime=Time.time-seq.PropertyFloat["indexEnterTime"];

        int lastIndex=index;
        if(index==0){
            collider2D.enabled=true;
            if(durationTime>=3f/30f){
                rig.velocity=new Vector2(lookAt.x*jumpDirectionSpeed.x,jumpDirectionSpeed.y);
                index=1;
            }else{
                rig.velocity=Vector2.zero;
            }
        }else
        if(index==1){
            collider2D.enabled=false;
            if(onGround && rig.velocity.y<=0){
                index=2;
            }
        }else
        if(index==2){
            collider2D.enabled=true;
            if(!seq.PropertyBool.ContainsKey("fired")){
                Fire(true);
                seq.PropertyBool.Add("fired",true);
            }
            if(durationTime>=4f/60f){
                rig.velocity=Vector2.zero;
                index=0;
                // Fire(true);
            }else{
                rig.velocity=Vector2.zero;
            }
        }
        if(lastIndex!=index){
            if(seq.PropertyFloat.ContainsKey("indexEnterTime")){
                seq.PropertyFloat.Remove("indexEnterTime");
            }
            if(seq.PropertyBool.ContainsKey("fired")){
                seq.PropertyBool.Remove("fired");
            }
        }
        seq.PropertyInteger["index"]=index;
        mainSR.sprite = jumpSprites[index];

    }

    private void RunFireBehavior(SeqAction seq)
    {
        if (seq.IsFirstAction())
        {
            seq.PropertyInteger.Add("lastFireIndex", 0);
        }
        WalkAction(seq.DurationTime);
        rig.velocity = lookAt.x * speed * Vector2.right;

        float moveDistance = 0f;
        if (lookAt.x == 1)
        {
            moveDistance = transform.position.x - leftLimit;
        }
        else if (lookAt.x == -1)
        {
            moveDistance = rightLimit - transform.position.x;
        }
        int lastFireIndex = seq.PropertyInteger["lastFireIndex"];
        int boxMagazine = 5;
        float fireInterval = (rightLimit - leftLimit) / (boxMagazine + 1);
        int fireIndex = Mathf.FloorToInt(moveDistance / fireInterval);
        if (fireIndex > 0 && fireIndex <= boxMagazine && fireIndex != lastFireIndex)
        {
            Vector2 firePosition = transform.position;
            firePosition += (17f / 16f) * Vector2.up;
            if (lookAt.x == 1)
            {
                firePosition.x = leftLimit + fireInterval * fireIndex;
            }
            else if (lookAt.x == -1)
            {
                firePosition.x = rightLimit - fireInterval * fireIndex;
            }
            Fire(firePosition,false);
            seq.PropertyInteger["lastFireIndex"] = fireIndex;


        }
    }

    private void LineUpFireBehavior(SeqAction seq)
    {
        float centerX=(leftLimit+rightLimit)/2f; //(96f + 32f) / 16f;
        if (seq.IsFirstAction())
        {   
            float space = 0.5f;
            float lineUpWidth=(countOfLineUp-1)*space;
            float targetPositionOffsetX =(countOfLineUp-noInLineUp)*space-(lineUpWidth/2f);
            seq.PropertyFloat.Add("targetPositionX",targetPositionOffsetX + centerX);
            seq.PropertyFloat.Add("startPositionX",transform.position.x);
            seq.PropertyBool.Add("inPlace",false);
            seq.PropertyFloat.Add("inPlaceTime",0);
            seq.PropertyBool.Add("goBack", false);
            seq.PropertyFloat.Add("goBackStartX",transform.position.x);
            seq.PropertyInteger.Add("lastFireIndex", -1);
        }
        bool inPlace = seq.PropertyBool["inPlace"];
        bool goBack = seq.PropertyBool["goBack"];
        if(!inPlace){
            WalkAction(seq.DurationTime);
            float startPositionX = seq.PropertyFloat["startPositionX"];
            float targetPositionX = seq.PropertyFloat["targetPositionX"];
            float currentX = transform.position.x;
            inPlace =
                (startPositionX < targetPositionX && currentX >= targetPositionX)
                || (startPositionX > targetPositionX && currentX <= targetPositionX)
            ;
            seq.PropertyBool["inPlace"]=inPlace;
            if(inPlace){
                seq.PropertyFloat["inPlaceTime"]=Time.time;
            }

        }else if(!goBack){
            mainSR.sprite=standUpSprite;
            rig.velocity=Vector2.zero;
            float inPlaceTime=seq.PropertyFloat["inPlaceTime"];
            if(Time.time-inPlaceTime>0.5f){
                seq.PropertyBool["goBack"]=true;
                seq.PropertyFloat["goBackStartX"]=transform.position.x;
            }
        }else if(goBack){
            float goBackStartX=seq.PropertyFloat["goBackStartX"];
            lookAt.x=centerX>goBackStartX?-1:1;

            float moveDistance = Mathf.Abs(transform.position.x - goBackStartX);
            int lastFireIndex = seq.PropertyInteger["lastFireIndex"];
            int boxMagazine = 2;
            float fireInterval = (centerX - leftLimit) / boxMagazine;
            int fireIndex = Mathf.FloorToInt(moveDistance / fireInterval);
            bool arrived=fireIndex >= 0 && fireIndex != lastFireIndex;
            
            if (arrived && !seq.PropertyFloat.ContainsKey("arrivedTime")){
                //到达开枪位置
                seq.PropertyFloat["arrivedTime"]=Time.time;
                mainSR.sprite = standUpSprite;
                rig.velocity = Vector2.zero;

            }else if(arrived)
            {
                //等待开枪
                mainSR.sprite = standUpSprite;
                rig.velocity = Vector2.zero;

                float arrivedTime=seq.PropertyFloat["arrivedTime"];
                if(Time.time-arrivedTime<0.5f){
                }else{
                    //开枪
                    Vector2 firePosition = transform.position;
                    firePosition += (17f / 16f) * Vector2.up;
                    Fire(firePosition, false);
                    seq.PropertyInteger["lastFireIndex"] = fireIndex;
                    seq.PropertyFloat.Remove("arrivedTime");

                }
            }else{
                //跑向下一个开枪位置
                WalkAction(seq.DurationTime);
            }            
        }

    }

    private void GrenadeFireBehavior(SeqAction seq)
    {
        if(seq.IsFirstAction()){
            float firstSpace=(rightLimit-leftLimit)/4;
            seq.PropertyFloat.Add("firstPlaceX",lookAt.x==1?leftLimit+firstSpace:rightLimit-firstSpace);
            seq.PropertyBool.Add("isFirstPlace",true);
            seq.PropertyBool.Add("inPlace",false);
            seq.PropertyBool.Add("canThrowGrenade",false);
            standAction=new(){keepingTime=1.5f};
            throwGrenadeAction=new(){intervalTime=0.3f};
        }
        bool isFirstPlace=seq.PropertyBool["isFirstPlace"];
        bool inPlace=seq.PropertyBool["inPlace"];
        bool canThrowGrenade=seq.PropertyBool["canThrowGrenade"];
        if(!inPlace){
            float targetPlaceX=seq.PropertyFloat["firstPlaceX"];
            if(!isFirstPlace){
                if(Utility.GetNearestPlayerPosition(transform.position,out Vector2 playerPosition)){
                    Vector2 targetPlace=camera3DLevel.TransformPlayerViewToEnemyView(playerPosition);
                    if(targetPlace.x-transform.position.x>=0){
                        lookAt.x=1;
                    }else{
                        lookAt.x=-1;
                    }
                    targetPlaceX=targetPlace.x-(lookAt.x*(8f/16f));
                }
            }
            WalkAction(seq.DurationTime);
            if(arrivedPlaceX(targetPlaceX)){
                seq.PropertyBool["inPlace"]=true;
                seq.PropertyBool["isFirstPlace"]=false;
                standAction.ReStart();
            }
        }else{
            StandAction();
            standAction.Update(null,(SeqAction seq2)=>{
                seq.PropertyBool["canThrowGrenade"]=true;
                throwGrenadeAction.ReStart();
            });
        }
        if(canThrowGrenade){
            StandAction();
            throwGrenadeAction.Update((SeqAction seq2)=>{
                ThrowGrenade();
                if(seq2.seqNo>=2){
                    seq2.done=true;
                    seq.PropertyBool["inPlace"]=false;
                    seq.PropertyBool["canThrowGrenade"]=false;
                }
            });
        }
    }

    private bool arrivedPlaceX(float placeX){
        return (transform.position.x-placeX)*lookAt.x>=0;
    }
    private void WalkAction(float time){
        int index = Mathf.FloorToInt(time/(4f / 60f)) % walkSprites.Length;
        mainSR.sprite = walkSprites[index];
        rig.velocity = lookAt.x * speed * Vector2.right;
        Vector2 scale = transform.localScale;
        scale.x = lookAt.x;
        transform.localScale = scale;
    }
    private void StandAction(){
        mainSR.sprite = standUpSprite;
        rig.velocity=Vector2.zero;
    }

    private void Fire(bool toPlayer)
    {
        Vector2 firePosition = transform.position;
        firePosition += (17f / 16f) * Vector2.up + (3f / 16f) * Vector2.right;
        Fire(firePosition, toPlayer);
    }
    private void Fire(Vector2 firePosition, bool toPlayer)
    {
        Vector2 targetPosition = Vector2.zero;
        bool canFire = true;
        if (toPlayer)
        {
            canFire = Utility.GetNearestPlayerPosition(transform.position, out targetPosition);
            if (canFire)
            {
                targetPosition.y = 48f / 16f;
            }
        }
        else
        {
            Rect startRect = new(96f, 112f, 64f, 18f);
            Rect targetRect = new(32f, 48f, 192f, 41f);
            targetPosition = Utility.Transform3DPosition(firePosition, startRect, targetRect);
        }
        if (canFire)
        {
            GameObject bulletObj = Instantiate(bulletPrefab, firePosition, Quaternion.identity);
            if (bulletObj.TryGetComponent(out ABullet bullet))
            {
                Vector2 dir = targetPosition - firePosition;
                dir.Normalize();
                bullet.Launch(dir);
            };
        }
    }

    private void ThrowGrenade(){
        Vector2 position=transform.position+Vector3.up*(22f/16f);
        Instantiate(Constant.Prefabs.Grenade3D.Load(),position,Quaternion.identity).TryGetComponent(out Grenade3D grenade);
    }

    public override void Run()
    {
        if (!running)
        {
            base.Run();
            Vector2 targetPosition = Camera.main.transform.position;
            float x = targetPosition.x - transform.position.x;
            lookAt = new Vector2(x >= 0 ? 1 : -1, 0);
        }
    }

    private void UpdateInViewport()
    {
        if (!appeared && InLimitRange())
        {
            appeared = true;
        }
        else if (appeared && !InLimitRange())
        {
            Destroy(gameObject);
        }
    }

    private bool InLimitRange()
    {
        bool ret = false;
        if (transform.TryGetComponent(out SpriteRenderer sr))
        {
            Bounds bounds = sr.bounds;
            ret = bounds.max.x >= leftLimit && bounds.min.x <= rightLimit;
        }
        return ret;
    }

    private void ChangeColor(bool isRed){
        if(TryGetComponent(out SpriteRenderer sr)){
            if(isRed){
                sr.material=Constant.Materials.Enemy3DSoldierRedColor.Load();
            }else{
                sr.material=Constant.Materials.Enemy3DSoldierNormalColor.Load();
            } 
        }
    }
    private void ThrowGun()
    {

        GameObject gunObj = Instantiate(Constant.Prefabs.GunAward.Load(), transform.position, Quaternion.identity);
        if (gunObj != null)
        {
            Gun gun = gunObj.GetComponent<Gun>();
            gun.SetBulletType(bulletType);
            gun.Throw();
        }
    
    }
}
